#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <dirent.h>
#include <sys/stat.h>
#include <pthread.h>
#include <time.h>

/* Porta su cui mettersi in ascolto */
#define PORT 3666

/* Numero massimo di connessioni accettabile */
#define MAXCONN 5

/* File contenente username e password degli utenti */
#define UTENTI "listautenti.txt"

/* Costanti di acknoledge */
#define ACK "accept"

#define NACK "refuse"


typedef struct {
	char* username;
	char* password;

}itemType;

typedef struct listNode listNode;

typedef listNode* List;

struct listNode {
	itemType item;
	List next;

};


void spezza(char*,char*,char*);
List nuovoNodo(char*,char*);
List creaLista(char*);
char* setPassword(char*);
char* setUsername(char*);
int contaChar(char*);
List cancellaNodo(List);
List  cancellaLista(List);

/* Legge il file e crea la lista  */
List creaLista(char* file){
	char buffer[130],user[65],psw[65];
	int i=0;
	List head=NULL,nodo_corrente=NULL;
	FILE* fd=fopen(file,"r");
	memset(buffer,0,130);
	memset(user,0,65);
	memset(psw,0,65);
	if(fd==NULL){
		printf("Il file %s non puo' essere aperto\n",file);
		return NULL;

	}else{
		while(fscanf(fd,"%s",buffer)!= EOF){
			spezza(buffer,user,psw);
			if(i==0){
				head=nuovoNodo(user,psw);
				nodo_corrente=head;

			}else{
				nodo_corrente->next=nuovoNodo(user,psw);
				nodo_corrente=nodo_corrente->next;

			}
			i++;
			memset(buffer,0,130);
			memset(user,0,65);
			memset(psw,0,65);

		}
		if(fclose(fd)==-1)perror("Chiusura non riuscita!");
		if(head==NULL)perror("Head vuoto\n");
		return head;

	}

}

/* Crea un nuovo nodo  */
List nuovoNodo(char* user,char* psw){
	List head;
	head=(List)malloc(sizeof(listNode));
	head->item.username=setUsername(user);
	head->item.password=setPassword(psw);
	head->next=NULL;
	return head;

}

/* Divide le stringa in username e password  */
void spezza(char * buffer,char* user,char* psw){
	int i,j;
	i=0;
	while(buffer[i]!=':'){
		user[i]=buffer[i];
		i++;

	}
	user[i]='\0';
	i++;
	j=0;
	while(buffer[i+j]!='\0'&&buffer[i+j]!='\n'){
		psw[j]=buffer[i+j];
		j++;

	}
	psw[j]='\0';
	return;

}

/* Alloca lo spazio per la username  */
char* setUsername(char* user){
	char* username;
	int n,i;
	n=contaChar(user);
	username=(char*)malloc(n*sizeof(char));
	for(i=0;i<=n;i++){
		username[i]=user[i];

	}
	return username;

}

/* Alloca lo spazio per la password  */
char* setPassword(char* psw){
	char* password;
	int n,i;
	n=contaChar(psw);
	password=(char*)malloc(n*sizeof(char));
	for(i=0;i<=n;i++){
		password[i]=psw[i];

	}
	return password;

}

/* Conta i caratteri di una stringa  */
int contaChar(char* stringa){
	int i=0;
	while(stringa[i]!='\0')i++;
	return i+1;

}

/* Dealloca il primo nodo della lista  */
List cancellaNodo(List lista){
	List head=lista->next,nodo=lista;
	free(nodo->item.password);
	free(nodo->item.username);
	free(nodo);
	return head;

}

/* Dealloca una lista  */
List  cancellaLista(List lista){
	while(lista!=NULL){
		lista=cancellaNodo(lista);

	}
	return lista;

}

/* Inizializzazione della struttura sockaddr_in */
void addr_init(struct sockaddr_in *addr, int port, long int ip){
	addr->sin_family=AF_INET;
	addr->sin_port=htons((u_short)port);
	addr->sin_addr.s_addr=ip;

}

int gestione_dir(List utenti){
	List ptr;
	char bu[MAXNAMLEN];
	mkdir("serverMail",S_IRWXU);
	chdir("serverMail");
	ptr=utenti;
	while(ptr!=NULL){
		mkdir(ptr->item.username,S_IRWXU);
		ptr=ptr->next;

	}
	return 0;

}

/* funzione che prende in input il messaggio e lo separa dall'header  */
int spacchetta_msg(char* buff,char* user,char* psw){
    printf("\nSPACCHETTA\n");
	int i,j,k;
	if((buff[0]!='<')||(buff[2]!='>')){
		perror("ERROR on the message");
		return -1;

	}else{
		i=3,j=0;
		while(buff[i+j]!=':'){
			user[j]=buff[i+j];
			j++;

		}
		user[j]='\0';
		j++;
		k=0;
		while(buff[i+j+k]!='\0'){
			psw[k]=buff[i+j+k];
			k++;

		}
		psw[k]='\0';

	}
	return 0;

}

int recuperaMittente(char* messaggio,char* mitt){
  int i=8,j=0;
  while(messaggio[i]!='\n'){
    i++;
  }
  i=i+10;
    while(messaggio[i]!='\n'){
      mitt[j]=messaggio[i];
      j++;
      i++;
  }
  mitt[j]='\0';
}

int recuperaDestinatario(char* messaggio,char* dest){
  int i=8,j=0;
  while(messaggio[i]!='\n'){
    dest[j]=messaggio[i];
    j++;
    i++;
  }
  dest[j]='\0';
  return 0;

}

void *sender(void *arg){


	return 0;

}

void *receiver(void *arg){
	FILE *fp;
	char user[65],*msg=NULL,*ptr,fileName[128],data[64];
	short int N;
	time_t rawtime;
	time (&rawtime);
	struct tm* leggibile;
	int *sd;

	sd=arg;

	/* Leggo la dimensione del messaggio */
	recv(*sd,(short int*)&N,sizeof(short int),0);

	N=ntohs(N);

	/* Salvo in un puntatore d'appoggio l'indirizzo di buff, in modo da poterlo recuperare se la riallocazione non dovesse andare a buon fine */
	ptr=msg;

	/* Alloco memoria per ricevere la stringa */

	msg=(char*)realloc(msg,N*sizeof(char));

	/* Verifico che l'allocazione sia andata a buon fine */
	if(msg==NULL){
		/* Dealloco lo spazio precedentemente assegnato a buff */
		free(ptr);
		perror("ERROR on realloc");
		exit(1);
	}

	/* Ricevo il messaggio */
	recv(*sd,msg,N*sizeof(char),0);

	recuperaDestinatario(msg,&user[0]);

	/* Cambio directory, mi porto nella directory del destinatario */
	chdir("serverMail");
	chdir(user);

	fileName[0]='\0';

	/* Recupero la data del sistema */
	leggibile = localtime(&rawtime);

	/* Salvo in una stringa la data nel formato dd-mm-yyyy_hh:mm */
	sprintf(data,"%d-%d-%d_%d:%d",leggibile->tm_mday,leggibile->tm_mon,leggibile->tm_year,leggibile->tm_hour,leggibile->tm_min);

	recuperaMittente(msg,&user[0]);
	strcat(strcat(fileName,data),user);

	/* Apro il file in scrittura */
	fp=fopen(fileName,"w");

	/* Copio nel file il contenuto del messaggio */
	fprintf(fp,"%s",msg);

	/* Chiudo il file */
	fclose(fp);

	/* Dealloco lo spazio assegnato al messaggio */
	free(msg);

	return 0;

}

int main(){
	int sd,new_sd;
	struct sockaddr_in server,client;
	int sock_size=sizeof(struct sockaddr_in);
	int pid,status,flag;
	int i,args;
	int N;
	char *buff=NULL,*ptr_c,usr[65],psw[65];
	List utenti=NULL,ptr;
	pthread_t tid;
	/* Creo la lista degli utenti, con username e password */
	utenti=creaLista(UTENTI);

	/*
	 * ptr=utenti;
	 * while(ptr!=NULL){
	 * printf("%s\n",ptr->item.username);
	 * printf("%s\n",ptr->item.password);
	 * ptr=ptr->next;}
	 */

	/* Creo le directory per la memorizzazione della posta di ogni utente */
	if(gestione_dir(utenti)<0){
		perror("ERROR on gestione_dir");
		exit(1);

	}

	/* Inizializzazione dell'indirizzo, con INADDR_ANY specifico che posso accettare connessioni da qualsiasi indirizzo  */
	addr_init(&server,PORT,INADDR_ANY);

	/* Creazione del socket */
	if((sd=socket(AF_INET,SOCK_STREAM,0))<0){
		perror("ERROR opening socket");
		exit(1);

	}

	/* Lego il socket appena creato all'indirizzo del server */
	if(bind(sd,(struct sockaddr*)&server,sizeof(server))<0){
		perror("ERROR on binding");
		exit(1);

	}

	printf("Server in ascolto sulla porta %d\n",PORT);

	/* Metto il server in ascolto */
	if(listen(sd,MAXCONN)<0){
		perror("ERROR on listen");
		exit(1);

	}

	printf("Server in ascolto - accetta fino a un massimo di %d connessioni\n",MAXCONN);

	/* Accetto connessioni finché ce ne sono */
	while(1) {

		/* Accetto le connessioni da parte del client creando un nuovo socket */
		if((new_sd=accept(sd,(struct sockaddr*)&client,&sock_size))<0){
			perror("ERROR on accept");
			exit(1);

		}

		printf("Connessione stabilita con successo con il client %d sulla porta %d\n",inet_ntoa(client.sin_addr.s_addr),ntohs(client.sin_port));

		/* Leggo la dimensione del messaggio */
		recv(new_sd,&N,sizeof(int),0);

		/*N=ntohs(N);*/
		/* Salvo in un puntatore d'appoggio l'indirizzo di buff, in modo da poterlo recuperare se la riallocazione non dovesse andare a buon fine */
		ptr_c=buff;

		/* Alloco memoria per ricevere la stringa */
		buff=(char*)realloc(buff,N*sizeof(char));

		/* Verifico che l'allocazione sia andata a buon fine */
		if(buff==NULL){

			/* Dealloco lo spazio precedentemente assegnato a buff */
			free(ptr_c);
			perror("ERROR on realloc");
			exit(1);

		}

		/* Ricevo la stringa */
		recv(new_sd,buff,N,0);

		/* Reupero la username e la password dal messaggio */
		if(spacchetta_msg(&buff[0],&usr[0],&psw[0])<0){
			perror("ERROR on the message");
			exit(1);
		}

		/* Caso in cui il cliet vuole ricevere i messaggi memorizzati nella casella di posta */
		if(buff[1]=='s'){

			/* Verifico le credenziali dell'utente */
			ptr=utenti;
			flag=0;
			while((ptr!=NULL)&&(!flag)){
				if((strcmp(usr,ptr->item.username)+(strcmp(psw,ptr->item.password))==0))
					flag=1;
				else
					ptr=ptr->next;

			}
			if(flag==1){

				/* invio ack al client */
				send(new_sd,ACK,strlen(ACK)+1,0);
				pthread_create(&tid,NULL,sender,(void *)&new_sd);
				pthread_detach(tid);

			}else{
				printf("Utente non autorizzato!");

				/* invio ack al client */
				send(new_sd,NACK,strlen(NACK)+1,0);

			}

		}else{

			/* Caso in cui il cliet vuole inviare un messaggio */
			if(buff[1]=='r'){

				/* Verifico le credenziali dell'utente */
				ptr=utenti;
				flag=0;
				while((ptr!=NULL)&&(!flag)){
					if((strcmp(usr,ptr->item.username)+(strcmp(psw,ptr->item.password))==0))
						flag=1;
					else
						ptr=ptr->next;

				}
				if(flag==1){
					/* invio ack al client */
					send(new_sd,ACK,strlen(ACK)+1,0);
					pthread_create(&tid,NULL,receiver,(void *)&new_sd);
					pthread_detach(tid);

				}else{
					printf("Utente non autorizzato!");

					/* invio ack al client */
					send(new_sd,NACK,strlen(NACK)+1,0);

				}

			}else{
				perror("ERROR on the message");
				exit(1);

			}

		}
		close(new_sd);




	}
	printf("\n");
	close(sd);
	free(buff);
	utenti=cancellaLista(utenti);
	if(utenti==NULL)printf("Deallocazione riuscita!\n");
	exit(0);

}
